package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	rand2 "math/rand"
	"my_code/biligoo/other"
	"net/url"
	"strconv"
	"time"

	"github.com/imroc/req"
)

func Login_SendSMS(tel string) (CAPTCHAKey string, IsNew bool) {
	numtel, _ := strconv.Atoi(tel)
	uri := "https://passport.bilibili.com/x/passport-login/sms/send"
	data := other.SignNew("appkey=783bbb7264451d82&build=7350200&buvid=XU4754240D381EA3E76104B472E516206AA80&c_locale=zh_CN&channel=pairui01&cid=86&device_tourist_id=20831378988735&disable_rcmd=0&local_id=XU4754240D381EA3E76104B472E516206AA80&login_session_id=" + randStr(32, 4, numtel) + "&mobi_app=android&platform=android&s_locale=zh_CN&spm_id=main.my-information.my-login.0&statistics=%7B%22appId%22%3A1%2C%22platform%22%3A3%2C%22version%22%3A%227.35.0%22%2C%22abtest%22%3A%22%22%7D&tel=" + tel + "&ts=" + other.StrTime())

	type Data struct {
		IsNew        bool   `json:"is_new"`
		CAPTCHAKey   string `json:"captcha_key"`
		RecaptchaURL string `json:"recaptcha_url"`
	}
	type SendSMS struct {
		Code    int64  `json:"code"`
		Message string `json:"message"`
		TTL     int64  `json:"ttl"`
		Data    Data   `json:"data"`
	}

	var body SendSMS
	client := req.C().SetUserAgent("bilibili").SetTimeout(5 * time.Second)
	resp, err := client.R(). //SetContentType("application/x-www-form-urlencoded").
					SetHeader("x-bili-trace-id", randStr(32, 4, numtel+1)+":"+randStr(16, 4, numtel)+":0:0").
		// SetHeader("buvid", randStr(32, 4, numtel)).
		SetHeader("env", "prod").
		SetHeader("app-key", "android64").
		SetHeader("user-agent", "Mozilla/5.0 BiliDroid/7.35.0 (bbcallen@gmail.com) os/android model/KB2000 mobi_app/android build/7350200 channel/pairui01 innerVer/7350210 osVer/13 network/1").
		// SetHeader("x-bili-trace-id", "598d49fc25509d530f57830fd8649fef:0f57830fd8649fef:0:0").
		SetHeader("x-bili-aurora-eid", "").
		SetHeader("x-bili-mid", "").
		SetHeader("x-bili-aurora-zone", "").
		SetHeader("x-bili-gaia-vtoken", "").
		SetHeader("bili-http-engine", "cronet").
		SetHeader("content-type", "application/x-www-form-urlencoded; charset=utf-8").
		// SetHeader("accept-encoding", "gzip, deflate, br").
		SetBody(data).SetResult(&body).Post(uri)
	if err != nil {
		fmt.Println("post err:", err)
		return
	}
	//fmt.Println(body.Message, body.Data.IsNew, body.Data.CAPTCHAKey)
	CAPTCHAKey = body.Data.CAPTCHAKey
	IsNew = body.Data.IsNew
	if false {
		fmt.Println("Login_SendSMS:", resp.String())
	}
	return
}

func LoginSmsByPrint(tel string) (access, refresh, cookie string, boo bool) {
	sRand()
	cKey, IsNew := Login_SendSMS(tel)
	sms := ""
	for i := 0; i < 30; i++ {
		if !IsNew && i == 0 {
			fmt.Printf("\r%v 不是新用户\n", tel)
			//break
		}
		//time.Sleep(time.Second * 2)
		fmt.Print("请输入验证码:")
		fmt.Scanln(&sms)
		if sms != "" {
			fmt.Println(tel, "验证码是", sms)
			pass := ""
			tel, pass = Login_NewPass(tel, sms)
			uri := "https://passport.bilibili.com/x/passport-login/login/sms"
			numtel, _ := strconv.Atoi(tel)
			data := other.SignNew("appkey=783bbb7264451d82&bili_local_id=c894f8398bae12ffb83b13fde135741820230103134445de4b9a8b05c5d84c9b&build=7350200&buvid=XU4754240D381EA3E76104B472E516206AA80&c_locale=zh_CN&captcha_key=" + cKey + "&channel=pairui01&cid=86&code=" + sms + "&device=phone&device_id=c894f8398bae12ffb83b13fde135741820230103134445de4b9a8b05c5d84c9b&device_meta=&device_name=HUAWEIKB2000&device_platform=Android13HUAWEIKB2000&device_tourist_id=20831378988735&disable_rcmd=0&dt=" + pass + "&from_pv=main.my-information.my-login.0.click&from_url=bilibili%3A%2F%2Fuser_center%2Fmine&local_id=XU4754240D381EA3E76104B472E516206AA80&login_session_id=" + randStr(32, 4, numtel) + "&mobi_app=android&platform=android&s_locale=zh_CN&spm_id=main.my-information.my-login.0&statistics=%7B%22appId%22%3A1%2C%22platform%22%3A3%2C%22version%22%3A%227.35.0%22%2C%22abtest%22%3A%22%22%7D&tel=" + tel + "&ts=" + other.StrTime())
			// Generated by https://quicktype.io

			type Cooky struct {
				Name     string `json:"name"`
				Value    string `json:"value"`
				HTTPOnly int64  `json:"http_only"`
				Expires  int64  `json:"expires"`
				Secure   int64  `json:"secure"`
				SameSite int64  `json:"same_site"`
			}
			type CookieInfo struct {
				Cookies []Cooky  `json:"cookies"`
				Domains []string `json:"domains"`
			}
			type TokenInfo struct {
				Mid          int64  `json:"mid"`
				AccessToken  string `json:"access_token"`
				RefreshToken string `json:"refresh_token"`
				ExpiresIn    int64  `json:"expires_in"`
			}
			type Data struct {
				Status     int64      `json:"status"`
				Message    string     `json:"message"`
				URL        string     `json:"url"`
				TokenInfo  TokenInfo  `json:"token_info"`
				CookieInfo CookieInfo `json:"cookie_info"`
				Sso        []string   `json:"sso"`
				IsNew      bool       `json:"is_new"`
				IsTourist  bool       `json:"is_tourist"`
			}
			type LoginSMS struct {
				Code    int64  `json:"code"`
				Message string `json:"message"`
				TTL     int64  `json:"ttl"`
				Data    Data   `json:"data"`
			}
			var body LoginSMS
			client := req.C().SetUserAgent("bilibili").SetTimeout(5 * time.Second)
			client.R().SetContentType("application/x-www-form-urlencoded").SetBody(data).SetResult(&body).Post(uri)

			//fmt.Println(body)

			//fmt.Println("Login_71:", resp.String())
			access = body.Data.TokenInfo.AccessToken
			refresh = body.Data.TokenInfo.RefreshToken

			for k := range body.Data.CookieInfo.Cookies {
				cookie1 := body.Data.CookieInfo.Cookies[k].Name
				cookie2 := body.Data.CookieInfo.Cookies[k].Value
				cookie = cookie + cookie1 + "=" + cookie2 + ";"
			}
			boo = true
			break
		}
	}
	return
}

var buvid string = "XY949639E2014B7564AEA9CB45C925203F9EB"                                //guid  local_id //37
var deviceid string = "Ol9oUTUENg5qUmJVKVUp"                                              //bili_local_id //20
var fingerprint string = "202201051824097e7acd2ee4e0fd2c491fb53722c9148e1ec13470c5d6b607" //local_fingerprint //14+48
func sRand() {
	buvid = "XY" + randStr(35, 0)
	deviceid = randStr(20, 2)
	fingerprint = time.Now().AddDate(0, 0, -1).In(time.FixedZone("CST", 28800)).Format("20060102150405") + randStr(48, 1)
}

// 0 数字+大写 1 数字+小写 2 数字+大小写 3 数字 4 5 16进制
func randStr(l, typ int, ran ...int) string {
	str := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	if typ == 1 {
		str = "0123456789qwertyuiopasdfghjklzxcvbnm"
	} else if typ == 2 {
		str = "0123456789qwertyuiopasdfghjklzxcvbnmABCDEFGHIJKLMNOPQRSTUVWXYZ"
	} else if typ == 3 {
		str = "0123456789"
	} else if typ == 4 {
		str = "0123456789abcdef"
	} else if typ == 5 {
		str = "0123456789ABCDEF"
	}
	bytes := []byte(str)
	result := []byte{}
	rr := time.Now().UnixNano()
	if len(ran) == 1 {
		rr = int64(ran[0])
	}
	r := rand2.New(rand2.NewSource(rr))

	for i := 0; i < l; i++ {
		ns := bytes[r.Intn(len(bytes))]
		if i == 0 && ns == bytes[0] {
			ns = bytes[1]
		}
		result = append(result, ns)
	}
	return string(result)
}
func Login_NewPass(name, pass string) (newName, newPass string) {
	newName = url.QueryEscape(name)
	uri := "https://passport.bilibili.com/x/passport-login/web/key?" + other.SignNew("appkey=783bbb7264451d82&build=7350200&buvid=XU4754240D381EA3E76104B472E516206AA80&c_locale=zh_CN&channel=pairui01&disable_rcmd=0&local_id=XU4754240D381EA3E76104B472E516206AA80&mobi_app=android&platform=android&s_locale=zh_CN&statistics=%7B%22appId%22%3A1%2C%22platform%22%3A3%2C%22version%22%3A%227.35.0%22%2C%22abtest%22%3A%22%22%7D&ts="+other.StrTime())

	type Data struct {
		Hash string `json:"hash"`
		Key  string `json:"key"`
	}
	type GetKey struct {
		Code    int64  `json:"code"`
		Message string `json:"message"`
		TTL     int64  `json:"ttl"`
		Data    Data   `json:"data"`
	}

	var body GetKey
	req.C().R().SetResult(&body).Get(uri)
	hash, key := body.Data.Hash, body.Data.Key

	aa, _ := RsaEncrypt([]byte(key), []byte(hash+pass))

	newPass = url.QueryEscape(base64.StdEncoding.EncodeToString(aa))

	return
}

func RsaEncrypt(publicKey []byte, origData []byte) ([]byte, error) {
	block, _ := pem.Decode(publicKey)
	if block == nil {
		return nil, errors.New("public key error")
	}
	pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	pub := pubInterface.(*rsa.PublicKey)
	return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
}
